#include "world/WorldFactory.h"
#include "world/NextVisitor.h"
#include "world/WorldSize.h"
#include "world/World.h"
#include "cell/Cell.h"
#include "cell/CellStateChar.h"
#include "cell/CellObject.h"
#include "position/Position.h"
#include "position/Direction.h"

namespace
{
    typedef int (*pCount)(CellObject*, const Position& pos);

    int isAlive(Cell& cell)
    {
        return cell.isAlive();
    }

    int zero(CellObject*, const Position&)
    {
        return 0;
    }

#include "position/DefCount.h"
#include "position/PositionMacro.h"
}

namespace
{
    struct CellBoard : Neighbor
    {
        CellBoard(): cells(0), num(0)
        {}

        ~CellBoard()
        {
            delete[] cells;
        }

        void init(int num)
        {
            this->num = num;
            cells = new CellObject[num];
        }

        void visitAll(CellVisitor& visitor)
        {
            for(int i = 0; i< num; i++)
            {
                visitor.visit(cells[i]);
            }
        }

        void active(const Position& pos, CellState state)
        {
            activeOneCell(cells[pos.getIndex()], pos, state);
        }

    private:
        OVERRIDE(int countNeighbors(const Position& pos))
        {
            return getCount(cells, pos);
        }

        OVERRIDE(Cell& getSrcCell(const Position& pos))
        {
            return cells[pos.getIndex()];
        }

    private:

        int getCount(CellObject* cells, const Position& pos)
        {
            int count = 0;

            #include "position/SeekMacro.h"
            #include "position/PositionMacro.h"

            return count;
        }

        void activeOneCell(Cell& cell, const Position& pos, CellState state)
        {
            cell.init(pos, state);
        }

    private:
        CellObject* cells;
        int num;
    };

    struct WorldGroup
    {
        WorldGroup() : index(0)
        {
        }

        void visitAllNew(CellVisitor& visitor)
        {
            getNew().visitAll(visitor);
        }

        void visitAllOld(CellVisitor& visitor)
        {
            getOld().visitAll(visitor);
        }

        void next()
        {
            NextVisitor evolve(getOld());
            visitAllNew(evolve);
            index = 1 -  index;
        }

        void init(const char* init
                , const CellStateChars& chars
                , const WorldSize& size)
        {
            getOld().init(size.getTotal());
            getNew().init(size.getTotal());
            for(int i = 0; i< size.getTotal(); i++)
            {
                const Position pos(i, size);
                CellState state = chars.getState(init[i]);
                getOld().active(pos, state);
                getNew().active(pos, state);
            }
        }

    private:
        CellBoard& getOld(){ return board[index]; }
        CellBoard& getNew(){ return board[1 - index]; }

    private:
        CellBoard board[2];
        int index;
    };

    struct OutputVisitor : CellVisitor
    {
        OutputVisitor(std::ostream& os, const CellStateChars& chars)
            : os(os), chars(chars){}

    private:
        OVERRIDE(void visit(Cell& cell))
        {
            const char outChars[] = {chars.getDeadChar()
                    , chars.getAliveChar()};
            os<<(outChars[cell.isAlive()]);
            if(cell.isEndl()) os<<std::endl;
        }

    private:
        std::ostream& os;
        const CellStateChars& chars;

    };
}

struct WorldObject : World
{
private:
    OVERRIDE(void startWith(const char* init
            , const CellStateChars& chars
            , const WorldSize& size))
    {
        group.init(init, chars, size);

    }

    OVERRIDE(void nextGeneration())
    {
        group.next();
    }

    OVERRIDE(void output(std::ostream& os, const CellStateChars& chars))
    {
        OutputVisitor visitor(os, chars);
        group.visitAllOld(visitor);
    }

private:
    WorldGroup group;
};

World& WorldFactory::create()
{
    static WorldObject world;
    return world;
}
